package cn.sylinx.web;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.Properties;
import java.util.Set;

import cn.sylinx.web.log.GLog;
import cn.sylinx.web.plugin.IPlugin;
import cn.sylinx.web.util.PackageUtil;
import cn.sylinx.web.util.Resources;
import cn.sylinx.web.util.StringUtil;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;

/**
 * verx.web starter
 *
 * @author hanqz
 */
public class Starter {

	public static String CONFIG_JVM_VAR = "vertx.web.config";
	public static String CONFIG_SYSTEM_VAR = "VERTX_WEB_CONFIG";

	public static void main(String[] args) throws Exception {

		// 加載配置
		String configPath = null;
		if (args != null && args.length > 0) {
			// 从启动参数获取路径
			configPath = args[0];
		}
		Config cfg = loadConfig(configPath);

		// 启动插件
		startPlugins(cfg);

		// Vertx容器
		Vertx v = Vertx.vertx();

		// 发布参数
		DeploymentOptions options = new DeploymentOptions();
		options.setInstances(cfg.getInstance() == 0 ? 1 : cfg.getInstance());
		options.setWorkerPoolSize(cfg.getVertxWorkerPoolSize());

		// HttpVerticle 运行参数
		JsonObject jsonObject = new JsonObject();
		jsonObject.put("httpPort", cfg.getHttpPort());
		jsonObject.put("mappingPackges", cfg.getMappingPackages());
		jsonObject.put("authPackages", cfg.getAuthPackages());

		options.setConfig(jsonObject);

		v.deployVerticle(HttpVerticle.class.getName(), options, result -> {

			if (result.succeeded()) {
				// 启动成功
				GLog.info("http server start ok at port:{}", cfg.getHttpPort());
			} else {
				// 失败
				GLog.error("http server start fail, msg: " + result.result());
			}
		});

	}

	/**
	 * 加载配置文件
	 * 
	 * @param configPath
	 *            配置文件路径
	 * @return
	 * @throws IOException
	 */
	private static Config loadConfig(String configPath) throws IOException {

		if (StringUtil.isBlank(configPath)) {
			// 如果为空，从JVM中读取变量
			// -Dvertx.web.config=/usr/local/config/config.properties
			configPath = System.getProperty(CONFIG_JVM_VAR);
		}

		if (StringUtil.isBlank(configPath)) {
			// 如果从JVM中没有获取到路径，则从操作系统中获取环境变量
			// VERTX_WEB_CONFIG=/usr/local/config/config.properties
			configPath = System.getenv(CONFIG_SYSTEM_VAR);
		}

		Properties p = null;

		if (StringUtil.isBlank(configPath)) {
			// 参数、JVM、操作系统 都没找到配置文件，则读取默认配置
			p = Resources.getResourceAsProperties("config.properties");

		} else {
			// 有配置路径
			File f = new File(configPath);
			try (InputStream fis = new FileInputStream(f)) {

				p = new Properties();
				p.load(fis);
			}
		}

		Config cfg = new Config();
		cfg.setAuthPackages(p.getProperty(Config.CONFIG_AUTH_PACAKGES));
		cfg.setHttpPort(Integer.valueOf(p.getProperty(Config.CONFIG_HTTP_PORT, "8080")));
		cfg.setInstance(Integer.valueOf(p.getProperty(Config.CONFIG_INSTANCE, "10")));
		cfg.setMappingPackages(p.getProperty(Config.CONFIG_MAPPING_PACKAGES));
		cfg.setPluginPackages(p.getProperty(Config.CONFIG_PLUGIN_PACKAGES));
		cfg.setVertxWorkerPoolSize(Integer.valueOf(p.getProperty(Config.CONFIG_VERTX_WORKER_POOLSIZE, "100")));

		// 加载外部配置文件
		String propertyPath = p.getProperty(Config.CONFIG_PROPERTY_PATH);
		if (StringUtil.isNotBlank(propertyPath)) {
			File f = new File(propertyPath);
			try (InputStream fis = new FileInputStream(f)) {

				Properties tmp = new Properties();
				tmp.load(fis);
				cfg.setProperties(tmp);
			}
		}

		return cfg;
	}

	/**
	 * 启动插件
	 * 
	 * @param cfg
	 *            Config
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws ClassNotFoundException
	 */
	private static void startPlugins(Config cfg)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException {

		String packages = cfg.getPluginPackages();

		if (StringUtil.isBlank(packages)) {
			GLog.info("no plugins config.");
			return;
		}

		String[] pkgs = packages.split(",");
		for (String pkg : pkgs) {

			startOnePackagePlugins(cfg, pkg);
		}
	}

	/**
	 * 启动一个包下的所有插件
	 * 
	 * @param cfg
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	private static void startOnePackagePlugins(Config cfg, String pkg)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException {

		Set<String> clzSet = PackageUtil.getPackageClass(pkg);

		Class<?> clz = null;
		for (String clzStr : clzSet) {

			clz = Class.forName(clzStr);

			// 不是接口、不是抽象类、继承自IPlugin
			if (!clz.isInterface() && !Modifier.isAbstract(clz.getModifiers()) && IPlugin.class.isAssignableFrom(clz)) {

				IPlugin p = (IPlugin) clz.newInstance();
				boolean bl = p.start(cfg);
				if (!bl) {
					GLog.error("plugin start error, IPlugin:{}", clzStr);
				} else {
					GLog.info("plugin:{} start OK", clzStr);
				}
			}

		}
	}
}
